<!DOCTYPE html>
<html lang="zh-cn">
  <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="author" content="Zhou Wei <zromyk@163.com>">
  <title>智能车上位机使用指南</title>
  <link rel="shortcut icon" href="/favicon.ico">
  <link rel="stylesheet" href="/style/html/pure.css">
  <link rel="stylesheet" href="/style/html/main.css">
  <link rel="stylesheet" href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.css">
  <!-- <link rel="stylesheet" href="https://apps.bdimg.com/libs/highlight.js/9.1.0/styles/default.min.css"> -->
<link rel="stylesheet" href="/style/article/highlight/default.min.css">
<link rel="stylesheet" href="/style/article/pell-1.0.6/dist/pell.css">

</head>
<body>
  <div id="menu-background"></div>
  <div id="menu">
    <div class="pure-menu pure-menu-horizontal">
  <ul class="pure-menu-list block-middle">
    <li class="pure-menu-item">
  <a class="pure-menu-heading" href="/index.html">ZROMYK</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/index.html">主页</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/archive/index.html">归档</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/download/index.html">下载</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/feedback/index.html">反馈</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="/public/about/index.html">关于我</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link" href="https://github.com/zromyk"><i class="fa fa-github" style="font-size:32px"></i></a>
</li>

  </ul>
</div>

  </div>
  <div id="layout">
    <div class="content">
      <div id="nav">
    <div id="navigation" class="navigation">
  <ul class="pure-menu-list">
    <li class="pure-menu-item">
  <a class="pure-menu-link nav1" onclick="animateByNav()" href="#_1">一、上位机使用的项目预览</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav1" onclick="animateByNav()" href="#_2">二、编译方式</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav1" onclick="animateByNav()" href="#_3">三、上位机使用方法</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#_4">上位机的几个功能介绍</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#smc">读取和创建 SMC 文件</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#smc_1">SMC 文件格式</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#smc_2">SMC 类</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#3-smc_cdm">3. SMC_CDM 完整程序</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav1" onclick="animateByNav()" href="#smc_cdm">四、SMC_CDM 中的一些算法介绍</a>
</li>
<li class="pure-menu-item">
  <a class="pure-menu-link nav2" onclick="animateByNav()" href="#_5">为什么要使用这种连通域算法？</a>
</li>

  </ul>
</div>

</div>
<div id="content-articles">
  <h1 id="智能车上位机使用指南" class="content-subhead">智能车上位机使用指南</h1>
  <p>
    <span>2021-01-31</span>
    <span><span class="post-category post-category-smart-car">Smart Car</span></span>
    <button id="button-markdownEditor" class="pure-button" onclick="markdownEditor()">启用编辑</button>
    <button id="button-save" class="pure-button" onclick="save()">保存</button>
  </p>
  <div id="content-articles-markdownEditor" style="display: none;">
    <h1>编辑 power by pell</h1>
    <div id="editor" class="pell"></div>
    <div style="margin-top:20px;">
        <h3>Text output:</h3>
        <div id="text-output"></div>
    </div>
    <div style="margin-top:20px;">
        <h3>HTML output:</h3>
        <pre id="html-output"></pre>
    </div>
  </div>
  <div id="content-articles-markdown">
    <h1 id="_1">一、上位机使用的项目预览</h1>
<div class="pure-table-scrollable"><table class="pure-table pure-table-horizontal">
<thead>
<tr>
<th>工程</th>
<th>大小</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="/public/download/project/SmartCar_SMC/BMP.zip">BMP.zip</a></td>
<td>70KB</td>
<td>BMP 图片处理库。</td>
</tr>
<tr>
<td><a href="/public/download/project/SmartCar_SMC/SMC.zip">SMC.zip</a></td>
<td>848KB</td>
<td>SMC 文件处理库。</td>
</tr>
<tr>
<td><a href="/public/download/project/SmartCar_SMC/SmcroIDE.zip">SmcroIDE.zip</a></td>
<td>22.7MB</td>
<td>SmcrolDE 上位机（Windows 平台）。</td>
</tr>
<tr>
<td><a href="/public/download/project/SmartCar_SMC/SmcroIDE_project.zip">SmcroIDE_project.zip</a></td>
<td>37KB</td>
<td>SmcrolDE 上位机 Qt 工程。</td>
</tr>
<tr>
<td><a href="/public/download/project/SmartCar_SMC/video.zip">video.zip</a></td>
<td>1.6MB</td>
<td>包含智能车赛道图像文件。</td>
</tr>
<tr>
<td>SMC_CDM.zip</td>
<td>1.2MB</td>
<td>智能车图像处理代码（在钉钉群获取），本工程建立在 SMC 工程之上。</td>
</tr>
<tr>
<td>TC264_UP.zip</td>
<td>3.1MB</td>
<td>TC264 三轮车代码（在钉钉群获取）。</td>
</tr>
</tbody>
</table></div>
<h1 id="_2">二、编译方式</h1>
<p>每个工程都可以直接使用CMake编译，如需使用CMake，请自行安装。</p>
<p>或者在其他编译器中新建工程，进行编译，如VS code，DEV C++等。</p>
<h1 id="_3">三、上位机使用方法</h1>
<p>打开 SmcroIDE 文件夹中的 SmcroIDE.exe 程序</p>
<p><img class="pure-img" alt="2021-01-31 19.55.01" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 19.55.01.jpg" /></p>
<p>可看到上位机的界面，目前上位机仅适用于图像显示。</p>
<p><img class="pure-img" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 20.14.37.jpg" alt="2021-01-31 20.14.37" style="zoom:50%;" /></p>
<h2 id="_4">上位机的几个功能介绍</h2>
<p>SmcroIDE 上位机可以打开 SMC 文件和 BMP 文件，也可以保存 SMC 和 BMP 文件。</p>
<p>打开之后已经创建了一张名为 “untitled.bmp” 的灰度图片，可以使用绘画工具绘图，在软件使用过程中可随时对 SMC 文件进行编辑（绘画），然后保存，即对赛道图像进行修改。</p>
<p>下面为使用过程中的一些预览图（可以同时打开两个SMC文件进行比较）：</p>
<p><img class="pure-img" alt="2021-01-31 17.33.27" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 17.33.27.jpeg" /></p>
<p><img class="pure-img" alt="2021-01-31 18.49.14" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 18.49.14.jpg" /></p>
<h2 id="smc">读取和创建 SMC 文件</h2>
<h2 id="smc_1">SMC 文件格式</h2>
<p><img class="pure-img" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 16.17.43.jpg" alt="截屏2021-01-31 16.17.43" /></p>
<p>SMC 文件由文件头和数据区两部分组成：</p>
<p><strong>文件头</strong></p>
<p>包含以下内容：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">typedef struct  // SMC文件头结构，16byte
{
    uint32_t sCompression; // 4byte，SMC文件的压缩类型
    // sCompression = SMC_GARY（0）或者 SMC_BINARY（1）
    uint32_t sFrames;      // 4byte，帧数
    uint32_t sWidth;       // 4byte，单帧宽度，以像素为单位
    uint32_t sHeight;      // 4byte，单帧高度，以像素为单位
}__attribute__((__packed__))BitmapHeadNode;
</code></span></code></pre>
<p>可以通过以下函数获取其值：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">/**
* @description: 得到SMC的压缩格式
*               得到SMC的帧数
*               得到SMC的宽度
*               得到SMC的高度
*/
inline uint32_t getCompression() {return head.sCompression;}
inline uint32_t getFrames() {return head.sFrames;}
inline uint32_t getWidth() {return head.sWidth;}
inline uint32_t getHeight() {return head.sHeight;}
</code></span></code></pre>
<p><strong>数据区</strong></p>
<p>有两种格式：<span style="overflow-x: auto; max-width:100%; display:inline;"><code>SMC_GARY（0）</code></span>或者 <span style="overflow-x: auto; max-width:100%; display:inline;"><code>SMC_BINARY（1）</code></span>，即灰度格式和二值化格式</p>
<div class="pure-table-scrollable"><table class="pure-table pure-table-horizontal">
<thead>
<tr>
<th>格式</th>
<th>值</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>SMC_GARY</td>
<td>0</td>
<td>每个字节表示一个像素的灰度值。</td>
</tr>
<tr>
<td>SMC_BINARY</td>
<td>1</td>
<td>每个字节表示8个像素的灰度值，即同一行的连续8个像素保存在一个字节的存储空间内。</td>
</tr>
</tbody>
</table></div>
<blockquote class="content-quote">
<p>注意：在二值化 SMC_BINARY（1）格式下，对于一行非8的倍数的图像，未进行测试，可能发生未知情况。</p>
</blockquote>
<h2 id="smc_2">SMC 类</h2>
<div class="pure-table-scrollable"><table class="pure-table pure-table-horizontal">
<thead>
<tr>
<th>格式</th>
<th>值</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>SMC_GARY</td>
<td>0</td>
<td>每个字节表示一个像素的灰度值。</td>
</tr>
<tr>
<td>SMC_BINARY</td>
<td>1</td>
<td><strong>每个字节表示一个像素的值（0或255）。</strong></td>
</tr>
</tbody>
</table></div>
<p><strong>如何使用SMC类：</strong></p>
<p>1、读取 SMC 文件</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">/**
* @description: 读取整个SMC文件 / 读取淡SMC单帧图像
* @param {type} frame {uint32_t} 第frame帧
* @return: bool 读取成功：true，失败：false
*/
bool read();
bool read(uint32_t frame);
</code></span></code></pre>
<p>2、写入 SMC 文件</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">/**
* @description: 写入SMC图像
* @param {type} filePath {std::string} 文件保存地址
*               binaryInfo {uint8_t *}写入的数据首地址
*               frame {int}写入数据所在帧，小于0时，会在文件末尾补帧
* @return: bool 写入成功：true，失败：false
*/
bool write(); // 直接保存文件
bool write(std::string filePath); // 另存为
bool write(uint8_t *binaryInfo, int frame = -1); // 一帧帧写入
</code></span></code></pre>
<p>3、使用框架</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">SMC video(&quot;pathName/fileName1.smc&quot;);
/* 你想要读取的文件路径，windows系统下，如果文件在可执行文件的同目录中，可以直接写文件名 */
if (video.read()) {
    SMC result(&quot;pathName/fileName2.smc&quot;, 
        video.getCompression(), 
        video.getFrames(), 
        video.getWidth(), 
        video.getHeight());

    for (int frame = 0; frame &lt; video.getFrames(); ++frame) {
        uint8_t *pbinary = video.getBinary() + frame * video.getWidth() * video.getHeight();

        for(uint32_t r = 0; r &lt; video.getHeight(); ++r) {
            for (uint32_t c = 0; c &lt; video.getWidth(); ++c) {
                ImageBinarizationData[r][c] 
                    = *(pbinary + r * video.getWidth() + c) &amp; 0x01;
                // ImageBinarizationData[r][c] = *(pbinary + r * video.getWidth() + c);
            }
        }

        // 操作每帧图像 
        ... 
        // 操作每帧图像  

        /* 将处理后的结果写入 result.smc 文件中 */
        result.write((uint8_t *)ImageBinarizationData);
    }
}
</code></span></code></pre>
<p>注意：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">// 像素 &amp; 0x01 将读取到的数据转化为二值化值（二值化的smc文件在smc类中，每个像素保存为0或255）
ImageBinarizationData[r][c] = *(pbinary + r * video.getWidth() + c) &amp; 0x01;
</code></span></code></pre>
<h2 id="3-smc_cdm">3. SMC_CDM 完整程序</h2>
<p>该程序是十五届智能车双车组三轮车摄像头处理程序，可供参考，个人感觉在电脑上完成图像算法的初步搭建比在车上调试更方便。在电脑上调试完成之后再到车上调试。</p>
<h1 id="smc_cdm">四、SMC_CDM 中的一些算法介绍</h1>
<p>在 SMC_CDM 文件夹中有一个 test 文件，里面有一个 img.c 程序，这是我尝试写连通域算法的测试程序。</p>
<p><img class="pure-img" alt="2021-01-31 20.56.19" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 20.56.19.jpg" /></p>
<p>对左图进行连通域处理，得到右图结果。</p>
<p>STEP1：在左图中逐行寻找连续的白色条块（到某一行全黑为止），结果如下：</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code>numOfRuns: 65

59 | 0, (10, 105)    
58 | 1, (12, 105)    
57 | 2, (13, 105)    
56 | 3, (13, 105)    
55 | 4, (14, 106)    
54 | 5, (15, 106)    
53 | 6, (16, 106)    
52 | 7, (17, 106)    
51 | 8, (19, 106)    
50 | 9, (21, 106)    
49 | 10, (0, 3)      11, (22, 119)   
48 | 12, (0, 6)      13, (25, 106)   14, (117, 119)  
47 | 15, (0, 6)      16, (26, 106)   17, (112, 119)  
46 | 18, (0, 8)      19, (28, 119)   
45 | 20, (0, 9)      21, (30, 119)   
44 | 22, (0, 9)      23, (31, 119)   
43 | 24, (0, 9)      25, (34, 119)   
42 | 26, (0, 9)      27, (35, 119)   
41 | 28, (0, 10)     29, (37, 119)   
40 | 30, (0, 11)     31, (40, 119)   
39 | 32, (0, 12)     33, (41, 119)   
38 | 34, (0, 10)     35, (43, 119)   
37 | 36, (0, 9)      37, (45, 119)   
36 | 38, (0, 4)      39, (46, 119)   
35 | 40, (0, 0)      41, (48, 119)   
34 | 42, (50, 119)   
33 | 43, (52, 118)   
32 | 44, (52, 115)   
31 | 45, (48, 112)   
30 | 46, (45, 108)   
29 | 47, (42, 105)   
28 | 48, (38, 102)   
27 | 49, (35, 101)   
26 | 50, (31, 100)   
25 | 51, (28, 100)   
24 | 52, (25, 100)   
23 | 53, (24, 100)   
22 | 54, (24, 98)    
21 | 55, (24, 68)    56, (75, 100)   
20 | 57, (24, 62)    58, (77, 100)   
19 | 59, (25, 55)    60, (79, 100)   
18 | 61, (27, 48)    62, (81, 94)    
17 | 63, (33, 46)    64, (84, 87)    
</code></span></code></pre>
<p>STEP2：寻找主道（你需要连通的第一个区块），很简单，最底下一行最长的那块就是，然后我们把它用数组标记一下就行。</p>
<p>STEP3：判断哪些区块是相连的。</p>
<p>从图像最底下一行往上判断，判断上方区块的最左边是否在下方区块最右边的左边 ，且上方区块的最右边是否在下方区块最左边的右边，如果成立，表示这两个区块是连通的，加入到等价数组里。</p>
<pre><code class="pre-wrap"><span style="overflow-x: auto; max-width:100%; display:inline;"><code class="language-c++">cdm-&gt;stRun[i] &lt;= cdm-&gt;enRun[j] - offset &amp;&amp; cdm-&gt;stRun[j] + offset &lt;= cdm-&gt;enRun[i]
// offset 可以用来控制连通强度，即连通点的最小数目
</code></span></code></pre>
<div class="pure-table-scrollable"><table class="pure-table pure-table-horizontal">
<thead>
<tr>
<th>示例</th>
<th>连通性</th>
</tr>
</thead>
<tbody>
<tr>
<td><img class="pure-img" alt="2021-01-31 21.11.33" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 21.11.33.jpg" /></td>
<td>连通</td>
</tr>
<tr>
<td><img class="pure-img" alt="2021-01-31 21.11.44" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 21.11.44.jpg" /></td>
<td>连通</td>
</tr>
<tr>
<td><img class="pure-img" alt="2021-01-31 21.12.02" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 21.12.02.jpg" /></td>
<td>连通</td>
</tr>
<tr>
<td><img class="pure-img" alt="2021-01-31 21.13.29" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 21.13.29.jpg" /></td>
<td>连通</td>
</tr>
<tr>
<td><img class="pure-img" alt="2021-01-31 21.12.13" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 21.12.13.jpg" /></td>
<td>不连通</td>
</tr>
</tbody>
</table></div>
<p>对于边界等情况可能要特殊处理。</p>
<p>STEP4：处理回拐，当操作完上面的这些步骤之后你可以得到一个基础的连通域，但是这个连通域并没有往回连通。</p>
<p><img class="pure-img" alt="2021-01-31 21.31.10" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 21.31.10.jpg" /></p>
<p>比如上面这种情况，你无法将右下角那块也加入到主道连通域里面，一个简单的做法是，到最上面一个区域后，再往回进行判断。</p>
<p><img class="pure-img" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/2021-01-31 21.34.02.jpg" alt="2021-01-31 21.34.02" style="zoom:50%;" /></p>
<p><strong>（test 中的 img.c 连通域为早期版本，最终算法请看 Element.cpp）</strong></p>
<h2 id="_5">为什么要使用这种连通域算法？</h2>
<p>可以遍历整个连通域数组，方便地对图像的进行切割和填补。</p>
<p>可以控制连通域的连通强度（后期甚至发现可以避免一些小反光，只与主道有几个像素连接，但是总面积却挺大的反光）</p>
<p><img class="pure-img" alt="截屏2021-02-16 15.54.05" src="https://zromyk.gitee.io/myblog-figurebed/post/智能车上位机使用指南.assets/截屏2021-02-16 15.54.05.png" /></p>
<p>也建议你尝试一下其他连通域算法。</p>
<blockquote class="content-quote">
<p>重点：学习过算法的话，很明显上面其实是一个【并查集】的应用，所以这个连通域算法应该可以改进得更加标准。</p>
</blockquote>
  </div>
</div>
 
    </div>
  </div>
  <div id="footer-background">
    <div id="footer">
      <div class="legal pure-g">
  <div class="pure-u-1 u-sm-1-2">
    <p class="legal-license"><a href="https://beian.miit.gov.cn/#/Integrated/index">浙ICP备2020038748号</a></p>
  </div>
  <div class="pure-u-1 u-sm-1-2">
    <p class="legal-links"><a href="https://github.com/zromyk">GitHub</a></p>
    <p class="legal-copyright">Copyright © 2021 Wei Zhou. 保留所有权利。</p>
  </div>
</div>
    </div>
  </div>
  <!-- <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> -->
  <script src="/style/html/jquery.min.js"></script>
  <script src='/style/article/latex/latest.js?config=TeX-MML-AM_CHTML'></script>
<!-- <script src="https://cdn.geogebra.org/apps/deployggb.js"></script> -->
<script src="/style/article/deployggb.js"></script>
<!-- <script src="https://apps.bdimg.com/libs/highlight.js/9.1.0/highlight.min.js"></script> -->
<script type="text/javascript">
  // 脚本：navigation 随鼠标移动自动变换宽度
  var element = document.getElementById("navigation"); // 获取要操作的元素
  var elementWidth = parseInt(getComputedStyle(element).width);
  var elementLeft = 0;
  var elementRight = 0;
  element.addEventListener('mouseenter', function (event) { // 添加鼠标按下事件的监听器
    elementLeft = element.getBoundingClientRect().left - 10;
    elementRight = element.getBoundingClientRect().left + elementWidth * 3;
    window.addEventListener('mousemove', resize); // 添加全局的鼠标移动事件的监听器
  });

  function resize(event) {
    var minWidth = elementWidth;
    var maxWidth = elementWidth * 2.5;
    // console.log(elementLeft, event.clientX, elementRight, event.clientX - elementLeft + elementWidth / 2);
    if (elementLeft <= event.clientX && event.clientX <= elementRight) {
      var width = event.clientX - elementLeft + elementWidth / 2;
      width = Math.min(width, maxWidth);
      width = Math.max(width, minWidth);
      element.style.width = width + 'px'; // 设置新的宽度样式属性
    }
    else {
      element.style.width = elementWidth + 'px'; // 设置新的宽度样式属性
      stopResize();
    }
  }

  function stopResize() {
    element.style.width = elementWidth + 'px'; // 设置新的宽度样式属性
    // console.log("stopResize", elementLeft, event.clientX, elementRight, event.clientX - elementLeft + elementWidth / 2);
    window.removeEventListener('mousemove', resize); // 移除鼠标移动事件的监听器
  }
</script>
<script src="/style/article/highlight/highlight.min.js"></script>
<script type="text/javascript">
  // 脚本：code语法高亮
  hljs.initHighlightingOnLoad();
</script>
<script>
  function animateByNav() {
    $("html").animate({
        scrollTop: ($(event.target.hash).offset().top - 52)
    }, 300);
  };
</script>
<script src="/style/article/pell-1.0.6/dist/pell.js"></script>
<script>
  // 脚本：自由编辑页面
  var editor = window.pell.init({
    element: document.getElementById('editor'),
    defaultParagraphSeparator: 'p',
    onChange: function(html) {
        document.getElementById('text-output').innerHTML = html
        document.getElementById('html-output').textContent = html
    }
  });

  function markdownEditor() {
    var articles = document.getElementById('content-articles-markdown');
    if (articles.getAttribute("contenteditable") == "true") {
        articles.setAttribute("contenteditable", "false");
        document.getElementById("content-articles-markdownEditor").style.display = "none"; //隐藏
        document.getElementById("button-markdownEditor").innerHTML = "启用编辑";
    } else {
        articles.setAttribute("contenteditable", "true");
        document.getElementById("content-articles-markdownEditor").style.display = ""; //显示
        document.getElementById("button-markdownEditor").innerHTML = "关闭编辑";
    }
  };

  function save() {
      window.alert("保存成功");
  };
</script>

</body>
</html>
